#include "pp_token.h"
#include "pp_utils.h"

#include <fstream>

namespace pp
{
    Tokenizer::Tokenizer() : m_u32Pos(0)
    {
        m_fnState = &Tokenizer::OnTokenStateText;
    }

    void Tokenizer::Reset()
    {
        m_u32Pos = 0;
        m_sName = "";
        m_fnState = &Tokenizer::OnTokenStateText;
        m_oTokens.clear();
    }

    std::vector<std::shared_ptr<TokenInfo> >& Tokenizer::Parse(const std::string& data)
    {
        Reset();

        if (data.empty()) return m_oTokens;

        uint32_t len = data.length();
        const char* bytes = data.data();

        for (uint32_t i = 0; i < len; i++)
        {
            (this->*m_fnState)(i, bytes[i], data);
        }

        return m_oTokens;
    }

    std::vector<std::shared_ptr<TokenInfo> >& Tokenizer::ParseFile(const std::string& fileName)
    {
        // 打开文件
        std::ifstream ifs;
        ifs.open(fileName, std::ios::in | std::ios::binary);
        if (!ifs.is_open())
        {
            throw std::runtime_error("error to open file:" + fileName);
        }

        // 读取文件
        std::string fileData;
        char readData[4096];

        while (!ifs.eof())
        {
            ifs.read(readData, sizeof(readData));
            fileData.append(readData, ifs.gcount());
        }

        ifs.close();

        return Parse(fileData);
    }

    void Tokenizer::OnTokenStateText(uint32_t pos, char ch, const std::string& data)
    {
        if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
        {
            m_sName = Trim(data.substr(m_u32Pos, pos - m_u32Pos));
            if (!m_sName.empty())
            {
                m_u32Pos = pos;
                m_fnState = &Tokenizer::OnTokenStateSpace;
                m_oTokens.push_back(std::make_shared<TokenInfo>(TOKEN_TYPE_TEXT, m_sName));
            }
        }
        else if (ch == '{' || ch == '}' || ch == ';' || ch == ',' || ch == '[' || ch == ']')
        {
            m_sName = Trim(data.substr(m_u32Pos, pos - m_u32Pos));
            if (!m_sName.empty()) m_oTokens.push_back(std::make_shared<TokenInfo>(TOKEN_TYPE_TEXT, m_sName));
            m_u32Pos = pos + 1;
            std::string val(1, ch);
            m_oTokens.push_back(std::make_shared<TokenInfo>(TOKEN_TYPE_OP, val));
        }
        else if (ch == '/')
        {
            m_sName = Trim(data.substr(m_u32Pos, pos - m_u32Pos));
            if (!m_sName.empty()) m_oTokens.push_back(std::make_shared<TokenInfo>(TOKEN_TYPE_TEXT, m_sName));
            m_fnState = &Tokenizer::OnTokenStateComment;
        }
        else if (ch == '=')
        {
            m_sName = Trim(data.substr(m_u32Pos, pos - m_u32Pos));
            if (!m_sName.empty()) m_oTokens.push_back(std::make_shared<TokenInfo>(TOKEN_TYPE_TEXT, m_sName));
            m_fnState = &Tokenizer::OnTokenStateRefer;
        }
        else if (ch == '`')
        {
            m_sName = Trim(data.substr(m_u32Pos, pos - m_u32Pos));
            if (!m_sName.empty()) m_oTokens.push_back(std::make_shared<TokenInfo>(TOKEN_TYPE_TEXT, m_sName));
            m_u32Pos = pos + 1;
            m_fnState = &Tokenizer::OnTokenStateTags;
        }
        else if (ch == '\'')
        {
            m_sName = Trim(data.substr(m_u32Pos, pos - m_u32Pos));
            if (!m_sName.empty()) m_oTokens.push_back(std::make_shared<TokenInfo>(TOKEN_TYPE_TEXT, m_sName));
            m_u32Pos = pos + 1;
            m_fnState = &Tokenizer::OnTokenStateSingleQuotes;
        }
        else if (ch == '\"')
        {
            m_sName = Trim(data.substr(m_u32Pos, pos - m_u32Pos));
            if (!m_sName.empty()) m_oTokens.push_back(std::make_shared<TokenInfo>(TOKEN_TYPE_TEXT, m_sName));
            m_u32Pos = pos + 1;
            m_fnState = &Tokenizer::OnTokenStateQuotes;
        }
    }

    void Tokenizer::OnTokenStateSpace(uint32_t pos, char ch, const std::string& data)
    {
        if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') return;

        if (ch == '{' || ch == '}' || ch == ';' || ch == ',' || ch == '[' || ch == ']')
        {
            std::string val(1, ch);
            m_oTokens.push_back(std::make_shared<TokenInfo>(TOKEN_TYPE_OP, val));
        }
        else if (ch == '/')
        {
            m_fnState = &Tokenizer::OnTokenStateComment;
        }
        else if (ch == '=')
        {
            m_fnState = &Tokenizer::OnTokenStateRefer;
        }
        else if (ch == '`')
        {
            m_u32Pos = pos + 1;
            m_fnState = &Tokenizer::OnTokenStateTags;
        }
        else if (ch == '\'')
        {
            m_u32Pos = pos + 1;
            m_fnState = &Tokenizer::OnTokenStateSingleQuotes;
        }
        else if (ch == '\"')
        {
            m_u32Pos = pos + 1;
            m_fnState = &Tokenizer::OnTokenStateQuotes;
        }
        else
        {
            m_u32Pos = pos;
            m_fnState = &Tokenizer::OnTokenStateText;
        }
    }

    void Tokenizer::OnTokenStateComment(uint32_t pos, char ch, const std::string& data)
    {
        if (ch == '/')
        {
            m_u32Pos = pos + 1;
            m_fnState = &Tokenizer::OnTokenStateComment2;
        }
        else
        {
            throw std::runtime_error("error encounter a single /");
        }
    }

    void Tokenizer::OnTokenStateComment2(uint32_t pos, char ch, const std::string& data)
    {
        if (ch == '\r' || ch == '\n')
        {
            m_sName = Trim(data.substr(m_u32Pos, pos - m_u32Pos));
            if (!m_sName.empty()) m_oTokens.push_back(std::make_shared<TokenInfo>(TOKEN_TYPE_COMMENT, m_sName));
            m_u32Pos = pos + 1;
            m_fnState = &Tokenizer::OnTokenStateText;
        }
    }

    void Tokenizer::OnTokenStateTags(uint32_t pos, char ch, const std::string& data)
    {
        if (ch == '`')
        {
            m_sName = Trim(data.substr(m_u32Pos, pos - m_u32Pos));
            if (!m_sName.empty()) m_oTokens.push_back(std::make_shared<TokenInfo>(TOKEN_TYPE_TAGS, m_sName));
            m_u32Pos = pos + 1;
            m_fnState = &Tokenizer::OnTokenStateText;
        }
    }

    void Tokenizer::OnTokenStateRefer(uint32_t pos, char ch, const std::string& data)
    {
        if (ch == '>')
        {
            m_oTokens.push_back(std::make_shared<TokenInfo>(TOKEN_TYPE_OP, "=>"));
            m_u32Pos = pos + 1;
            m_fnState = &Tokenizer::OnTokenStateText;
        }
        else if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
        {
            m_oTokens.push_back(std::make_shared<TokenInfo>(TOKEN_TYPE_OP, "="));
            m_fnState = &Tokenizer::OnTokenStateSpace;
        }
        else if (ch == '/')
        {
            m_oTokens.push_back(std::make_shared<TokenInfo>(TOKEN_TYPE_OP, "="));
            m_fnState = &Tokenizer::OnTokenStateComment;
        }
        else if (ch == '`')
        {
            m_oTokens.push_back(std::make_shared<TokenInfo>(TOKEN_TYPE_OP, "="));
            m_fnState = &Tokenizer::OnTokenStateTags;
        }
        else
        {
            m_oTokens.push_back(std::make_shared<TokenInfo>(TOKEN_TYPE_OP, "="));
            m_u32Pos = pos;
            m_fnState = &Tokenizer::OnTokenStateText;
        }
    }

    void Tokenizer::OnTokenStateSingleQuotes(uint32_t pos, char ch, const std::string& data)
    {
        if (ch == '\'')
        {
            m_sName = Trim(data.substr(m_u32Pos, pos - m_u32Pos));
            if (!m_sName.empty()) m_oTokens.push_back(std::make_shared<TokenInfo>(TOKEN_TYPE_QUOTES, m_sName));
            m_u32Pos = pos + 1;
            m_fnState = &Tokenizer::OnTokenStateText;
        }
    }

    void Tokenizer::OnTokenStateQuotes(uint32_t pos, char ch, const std::string& data)
    {
        if (ch == '\"')
        {
            m_sName = Trim(data.substr(m_u32Pos, pos - m_u32Pos));
            if (!m_sName.empty()) m_oTokens.push_back(std::make_shared<TokenInfo>(TOKEN_TYPE_QUOTES, m_sName));
            m_u32Pos = pos + 1;
            m_fnState = &Tokenizer::OnTokenStateText;
        }
    }
};
